package com.abo.yaas.webservice.client;

import java.util.ArrayList;
import java.util.List;

import com.abo.yaas.domain.YaasPojo;
import com.abo.yaas.webservice.constants.URLConstants;
import com.abo.yaas.webservice.exceptions.CommunicationException;
import com.abo.yaas.webservice.messageproviders.ClassWrapper;
import com.abo.yaas.webservice.messageproviders.ListWrapper;
import com.sun.jersey.api.client.GenericType;
import com.sun.jersey.api.client.UniformInterfaceException;
import com.sun.jersey.api.client.WebResource;
import com.sun.jersey.core.util.MultivaluedMapImpl;
import com.vaadin.addon.jpacontainer.EntityProvider;
import com.vaadin.addon.jpacontainer.Filter;
import com.vaadin.addon.jpacontainer.SortBy;

@SuppressWarnings("serial")
public class EntityProviderProxy<T extends YaasPojo> implements EntityProvider<T> {
    private final String serviceUri;

    private final WebResource service;

    private final Class<T> clazz;

    private static final GenericType<ClassWrapper<Long>> longType = new GenericType<ClassWrapper<Long>>() {
    };

    private static final GenericType<ClassWrapper<Boolean>> boolType = new GenericType<ClassWrapper<Boolean>>() {
    };

    public EntityProviderProxy(Class<T> className, final WebResource resource) {
        clazz = className;
        service = resource;
        serviceUri = "api/v1.0/" + resource.getProperties().get(className.getSimpleName());
    }

    @Override
    public boolean containsEntity(Object id, Filter filter) {
        final MultivaluedMapImpl params = new MultivaluedMapImpl();
        prepareFilter(params, filter);
        return servicePath(URLConstants.containsId).path(id.toString()).queryParams(params).get(boolType).getValue();
    }

    @SuppressWarnings("unchecked")
    @Override
    public List<Object> getAllEntityIdentifiers(Filter filter, List<SortBy> sortby) {
        final MultivaluedMapImpl params = new MultivaluedMapImpl();
        prepareFilter(params, filter);
        final List<Object> longRes = servicePath(URLConstants.entityIdList).queryParams(params).get(ListWrapper.class)
                .getValue();
        return new ArrayList<Object>(longRes);
    }

    @Override
    public T getEntity(Object id) {
        final T result = servicePath(null).path(URLConstants.getById).path(id.toString()).get(clazz);
        return result;
    }

    @Override
    public int getEntityCount(Filter filter) {
        final MultivaluedMapImpl params = new MultivaluedMapImpl();
        prepareFilter(params, filter);
        final int count = servicePath(URLConstants.entityCount).queryParams(params).get(longType).getValue().intValue();
        return count;
    }

    @Override
    public Object getEntityIdentifierAt(Filter filter, List<SortBy> sortby, int idx) {
        final MultivaluedMapImpl params = new MultivaluedMapImpl();
        prepareFilter(params, filter);
        final Object result = servicePath(URLConstants.entityId).path(String.valueOf(idx)).queryParams(params)
                .get(longType).getValue();
        return result;
    }

    @Override
    public Object getFirstEntityIdentifier(Filter filter, List<SortBy> sortby) {
        final MultivaluedMapImpl params = new MultivaluedMapImpl();
        prepareFilter(params, filter);
        return servicePath(URLConstants.firstEntityId).queryParams(params).get(longType).getValue();
    }

    @Override
    public Object getLastEntityIdentifier(Filter filter, List<SortBy> sortby) {
        final MultivaluedMapImpl params = new MultivaluedMapImpl();
        prepareFilter(params, filter);
        return servicePath(URLConstants.lastEntityId).queryParams(params).get(longType).getValue();
    }

    @Override
    public Object getNextEntityIdentifier(Object id, Filter filter, List<SortBy> sortby) {
        final MultivaluedMapImpl params = new MultivaluedMapImpl();
        prepareFilter(params, filter);
        return servicePath(URLConstants.nextEntityId).path(id.toString()).queryParams(params).get(longType).getValue();
    }

    @Override
    public Object getPreviousEntityIdentifier(Object id, Filter filter, List<SortBy> sortby) {
        final MultivaluedMapImpl params = new MultivaluedMapImpl();
        prepareFilter(params, filter);
        return servicePath(URLConstants.previousEntityId).path(id.toString()).queryParams(params).get(longType)
                .getValue();
    }

    @Override
    public boolean isEntitiesDetached() {
        return true;
    }

    protected void prepareFilter(MultivaluedMapImpl params, Filter filter) {
        // NOP, Override
    }

    public T saveEntity(final T entity) throws CommunicationException {
        T result = null;
        try {
            result = servicePath(null).post(clazz, entity);
        } catch (final UniformInterfaceException e) {
            throw new CommunicationException(e);
        }
        return result;
    }

    protected WebResource servicePath(String param) {
        return param != null ? service.path(serviceUri).path(param) : service.path(serviceUri);
    }

    @Override
    public void setEntitiesDetached(boolean arg0) throws UnsupportedOperationException {
    }
}
